from sklearn.ensemble import IsolationForest
from sklearn.preprocessing import StandardScaler
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import rcParams

# 设置字体为SimHei（黑体），解决matplotlib中文显示问题
rcParams['font.sans-serif'] = ['SimHei']
rcParams['axes.unicode_minus'] = False  # 解决负号显示问题


def outlier_detection(xlsx_file, feature1, feature2, select_feature, output_file="outlier_detection.png"):
    """
    异常点检测函数。

    参数：
    - xlsx_file: 数据文件路径，支持.xlsx文件。
    - select_feature: 要分析的目标列名称列表。
    - output_file: 输出图像文件路径，默认为"outlier_detection.png"。
    """
    # 加载数据集
    df = pd.read_excel(xlsx_file)

    # 打印列名，确保列名正确
    print("列名：", df.columns)

    # 移除列名中的前后空格（如果有的话）
    df.columns = df.columns.str.strip()

    # 检查select_feature中的列是否存在
    for feature in select_feature:
        if feature not in df.columns:
            print(f"列 '{feature}' 不存在，请检查列名！")
            return

    # 选择数据中的数值列（排除日期类型列等非数值列）
    numeric_df = df[select_feature].select_dtypes(include=[np.number])

    # 如果没有数值型数据，则退出
    if numeric_df.empty:
        print("没有找到数值型数据！")
        return

    # 数据标准化，因为IsolationForest对特征分布较为敏感
    scaler = StandardScaler()
    X_scaled = scaler.fit_transform(numeric_df)

    # 设置孤立森林模型参数
    clf = IsolationForest(contamination=0.1, random_state=42)  # contamination表示估计的异常比例
    clf.fit(X_scaled)  # 训练模型

    # 预测异常点：1 表示正常，-1 表示异常
    predictions = clf.predict(X_scaled)
    df['anomaly'] = predictions  # 将预测结果添加到DataFrame中

    # 统计并打印异常点信息
    anomalies_indices = [index for index, value in enumerate(df['anomaly']) if value == -1]
    for ai in anomalies_indices:
        print(f"有异常的数据位于{ai}行")
    print("异常点数量:", len(anomalies_indices))
    print("正常点数量:", (df['anomaly'] == 1).sum())

    # 生成列表 -1表示异常点，1表示正常
    results = [[row[feature1], row[feature2], row['anomaly']] for _, row in df.iterrows()]

    # 可视化部分特征
    plt.figure(figsize=(10, 6))  # 创建图形
    plt.scatter(df[feature1][df['anomaly'] == 1], df[feature2][df['anomaly'] == 1],
                label="正常点", alpha=0.6)  # 正常点散点图
    plt.scatter(df[feature1][df['anomaly'] == -1], df[feature2][df['anomaly'] == -1],
                label="异常点", color="red", alpha=0.8)  # 异常点散点图
    plt.xlabel(feature1)
    plt.ylabel(feature2)
    plt.title("异常点检测可视化")
    plt.legend()  # 显示图例
    plt.show()  # 展示图像
    plt.savefig(output_file)  # 保存图像
    print(f"散点图已保存为: {output_file}")
    print(results)


# 调用函数，传入参数
outlier_detection("模版.xlsx", "地区", "水泥掺量",
                  ["生产日期", "石5-10mm掺量"])  # 假设 '生产日期' 为 datetime 列
